// Package wechat
/**
* @Project : geektime-basic-go-study
* @File    : service.go
* @IDE     : GoLand
* @Author  : Tvux
* @Date    : 2024/10/25 09:19
**/

package wechat

import (
	"context"
	"encoding/json"
	"fmt"
	"geektime-basic-go-study/webook/backend/internal/domain"
	"log"
	"net/http"
	"net/url"
)

var redirectURI = url.PathEscape("https://myWebook.com/oauth2/wechat/callback")

type Service interface {
	GetAuthURL(ctx context.Context, state string) (string, error)
	GetWechatUserInfoByCode(ctx context.Context, code string) (domain.WechatUserInfo, error)
}

var (
	_ Service = (*ServiceImpl)(nil)
)

type ServiceImpl struct {
	appId     string
	appSecret string
	client    *http.Client
}

func NewService(appId string, appSecret string) Service {
	return &ServiceImpl{
		appId:     appId,
		appSecret: appSecret,
		client:    http.DefaultClient,
	}
}

func (Self *ServiceImpl) GetAuthURL(ctx context.Context, state string) (string, error) {
	const authURLPattern = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect"
	return fmt.Sprintf(authURLPattern, Self.appId, redirectURI, state), nil
}

// GetWechatUserInfoByCode 使用 code 获取 access_token 并获取微信登录用户信息并返回
func (Self *ServiceImpl) GetWechatUserInfoByCode(ctx context.Context, code string) (domain.WechatUserInfo, error) {
	const tokenURLPattern = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code"
	tokenURL := fmt.Sprintf(tokenURLPattern, Self.appId, Self.appSecret, code)
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, tokenURL, nil)
	if err != nil {
		return domain.WechatUserInfo{}, err
	}

	resp, err := Self.client.Do(req)
	if err != nil {
		return domain.WechatUserInfo{}, err
	}

	decoder := json.NewDecoder(resp.Body)
	var res Result
	if err := decoder.Decode(&res); err != nil {
		return domain.WechatUserInfo{}, err
	}

	if res.ErrCode != 0 {
		return domain.WechatUserInfo{}, fmt.Errorf("微信返回错误响应，错误码：%d，错误信息：%s", res.ErrCode, res.ErrMsg)
	}

	log.Printf("调用微信，拿到用户信息，openid：%s，unionid：%s", res.OpenId, res.UnionId)
	return domain.WechatUserInfo{
		OpenId:  res.OpenId,
		UnionId: res.UnionId,
	}, nil
}

type Result struct {
	ErrCode int64  `json:"errcode"`
	ErrMsg  string `json:"errmsg"`

	AccessToken  string `json:"access_token"`
	ExpiresIn    int64  `json:"expires_in"`
	RefreshToken string `json:"refresh_token"`

	OpenId  string `json:"openid"`
	Scope   string `json:"scope"`
	UnionId string `json:"unionid"`
}
